﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using HtmlAgilityPack;


namespace ChanSharp
{
    public class Post
    {
        private HtmlDocument document;
        private bool ancient;

        private List<string> Links; //TODO: this

        private string cachedID;
        private string cachedName;
        private string cachedEmail;
        private string cachedSubject;
        private string cachedComment;
        private DateTime cachedTime = DateTime.MinValue;
        private string cachedTripcode;
        private AttachedFile cachedFile;
        private bool cachedDeletedFile;

        private bool didCacheDeletedFile;
        private bool didCacheFile;

        #region post properties and html parsers

        public string RawHTML { get; set; }

        public string ID
        {
            get
            {
                if (cachedID == null)
                {
                    cachedID = ParseID();
                }
                return cachedID;
            }
        }

        private string ParseID()
        {
            string ID = nodeContainer.GetAttributeValue("id", "!").Substring(2);
            return ID;
        }

        public string Name
        {
            get
            {
                if (cachedName == null)
                {
                    cachedName = ParseName();
                }
                return cachedName;
            }
        }

        private string ParseName()
        {
            if (Email == string.Empty)
                return nodePostInfoNameBlock.SelectSingleNode("span[@class='name']").InnerText;
            else
                return nodePostInfoNameBlock.SelectSingleNode("a[@href]").SelectSingleNode("span[@class='name']").InnerText;
        }

        public string Email
        {
            get
            {
                if (cachedEmail == null)
                {
                    cachedEmail = ParseEmail();
                }
                return cachedEmail;
            }
        }

        private string ParseEmail()
        {
            if (nodeEmail == null)
                return string.Empty;
            else
                return nodeEmail.GetAttributeValue("href", "!");
        }

        public string Subject
        {
            get
            {
                if (cachedSubject == null)
                {
                    cachedSubject = ParseSubject();
                }
                return cachedSubject;
            }
        }

        private string ParseSubject()
        {
            if (IsOP)
                return nodePostInfo.SelectSingleNode("span[@class='subject']").InnerText;
            else
                return nodePostInfo.SelectSingleNode("span[@class='subject']").InnerText; //SelectSingleNode("span[@class='userInfo']").SelectSingleNode("span[@class='subject']").InnerText;
        }

        public string Comment
        {
            get
            {
                if (cachedComment == null)
                {
                    cachedComment = ParseComment();
                }
                return cachedComment;
            }
        }

        private string ParseComment()
        {
            HtmlNode nodeBlockQuote = nodeInnerContainer.SelectSingleNode("blockquote");
            return System.Web.HttpUtility.HtmlDecode(nodeBlockQuote.InnerText);
        }

        public DateTime Time
        {
            get
            {
                if (cachedTime == DateTime.MinValue)
                {
                    cachedTime = ParseTime();
                }
                return cachedTime;
            }
        }

        private DateTime ParseTime()
        {
            HtmlNode nodeTime = nodePostInfo.SelectSingleNode("span[@class='dateTime']");
            string utc = nodeTime.GetAttributeValue("data-utc", "!");
            return Thread.UnixTimeStampToDateTime(double.Parse(utc));
        }

        public string Tripcode
        {
            get
            {
                if (cachedTripcode == null)
                {
                    cachedTripcode = ParseTripcode();
                }
                return cachedTripcode;
            }
        }

        private string ParseTripcode()
        {
            HtmlNode nodeTripcode;

            if (Email == string.Empty)
                nodeTripcode = nodePostInfoNameBlock.SelectSingleNode("span[@class='postertrip']");
            else
                nodeTripcode = nodeEmail.SelectSingleNode("span[@class='postertrip']");

            if (nodeTripcode == null) //no tripcode
                return "";
            else
                return nodeTripcode.InnerText;
        }

        public AttachedFile File
        {
            get
            {
                if (!didCacheFile)
                {
                    cachedFile = ParseFile();
                    didCacheFile = true;
                }
                return cachedFile;
            }
        }

        private AttachedFile ParseFile()
        {
            AttachedFile File = null;

            //bool DeletedFile = false;

            if (nodeFile != null)
            {
                if (nodeFileInfo == null)
                    ; //DeletedFile = true;
                else
                {
                    HtmlNode nodeFileText = nodeFileInfo.SelectSingleNode("span[@class='fileText']");
                    bool IsSpoilered = nodeFile.SelectSingleNode("a").GetAttributeValue("class", "!") == "fileThumb imgspoiler";

                    HtmlNode nodeFileThumb;
                    if (IsSpoilered)
                    {
                        nodeFileThumb = nodeFile.SelectSingleNode("a[@class='fileThumb imgspoiler']");
                    }
                    else
                    {
                        nodeFileThumb = nodeFile.SelectSingleNode("a[@class='fileThumb']");
                    }

                    string fileURL = "http:" + nodeFileText.SelectSingleNode("a[@href]").GetAttributeValue("href", "!");
                    string fileChanFilename = nodeFileText.SelectSingleNode("a[@href]").InnerText;
                    string fileOriginalFilename;

                    if (IsSpoilered)
                        fileOriginalFilename = nodeFileText.GetAttributeValue("title", "!");
                    else
                        fileOriginalFilename = nodeFileText.SelectSingleNode("span[@title]").GetAttributeValue("title", "!");
                    string fileMd5 = nodeFileThumb.SelectSingleNode("img[@src]").GetAttributeValue("data-md5", "!");
                    string fileFilesize = nodeFileThumb.SelectSingleNode("img[@src]").GetAttributeValue("alt", "!");

                    File = new AttachedFile(fileURL, fileChanFilename, fileOriginalFilename, fileMd5, fileFilesize, IsSpoilered);
                }
            }
            return File;
        }

        public bool DeletedFile
        {
            get
            {
                if (!didCacheDeletedFile)
                {
                    cachedDeletedFile = ParseDeletedFile();
                    didCacheDeletedFile = true;
                }
                return cachedDeletedFile;
            }
        }

        private bool ParseDeletedFile()
        {
            bool DeletedFile = false;

            if (nodeFile != null && nodeFileInfo == null)
            {
                DeletedFile = true;
            }

            return DeletedFile;

        }

        #endregion

        #region node macros

        public HtmlNode nodeContainer
        {
            get
            {
                return document.DocumentNode.SelectSingleNode("div[@class]");
            }
        }

        public HtmlNode nodePostInfoNameBlock
        {
            get
            {
                if (IsOP)
                    return nodePostInfo.SelectSingleNode("span[@class='nameBlock']");
                return nodePostInfo.SelectSingleNode("span[@class='nameBlock']");
            }
        }

        public HtmlNode nodePostInfo
        {
            get
            {
                return nodeInnerContainer.SelectSingleNode("div[@class='postInfo desktop']");
            }
        }

        public HtmlNode nodeInnerContainer
        {
            get
            {
                if (IsOP)
                    return nodeContainer.SelectSingleNode("div[@class='post op']");
                else
                    return nodeContainer.SelectSingleNode("div[@class='post reply']");
            }
        }

        public HtmlNode nodeEmail
        {
            get
            {
                return nodePostInfoNameBlock.SelectSingleNode("a[@href]");
            }
        }

        public HtmlNode nodeFile
        {
            get
            {
                return nodeInnerContainer.SelectSingleNode("div[@class='file']");
            }
        }

        public HtmlNode nodeFileInfo
        {
            get
            {
                return nodeFile.SelectSingleNode("div[@class='fileInfo']");
            }
        }

        #endregion

        #region easy macros

        public string OriginalFilename
        {
            get
            {
                if (File == null)
                    return string.Empty;
                return File.OriginalFilename;
            }
        }

        public bool IsOP
        {
            get
            {
                return nodeContainer.GetAttributeValue("class", "!") == "postContainer opContainer"; //ID == ThreadID;
            }
        }

        public bool IsAnonymous
        {
            get
            {
                return Name == "Anonymous" && Tripcode == "";
            }
        }

        public string TimeFormattedAsChan
        {
            get
            {
                return Time.ToString("MM/dd/yy(ddd)HH:mm");
            }
        }

        public bool IsSage
        {
            get
            {
                return Email == "sage";
            }
        }

        public bool IsWithinTime(DateTime before, DateTime after)
        {
            return after <= Time && Time <= before;
        }

        public bool IsWithinID(int postMin, int postMax)
        {
            int id = int.Parse(ID);
            return postMin <= id && id <= postMax;
        }

        #endregion

        public Post(string rawHTML, bool ancient)
        {
            this.RawHTML = rawHTML;
            document = new HtmlDocument();
            document.LoadHtml(rawHTML);
            this.ancient = ancient;
        }

        public bool RemoveSideArrows()
        {
            HtmlAgilityPack.HtmlNode sideArrows = document.DocumentNode.SelectSingleNode("//div[@class='sideArrows']");

            if (sideArrows != null)
            {
                sideArrows.ParentNode.RemoveChild(sideArrows); //none of that &gt;&gt; bullshit, it wastes space
                return true;
            }

            return false;
        }

        public override string ToString()
        {
            string output = ID + " " + Name + ((string.IsNullOrWhiteSpace(Tripcode)) ? "" : (" " + Tripcode)) + " : " + Comment;
            return output;
        }

        /* old parsing technique
        //private Post PostContainerToPost(HtmlNode nodeContainer)
        //{
        //    //----==== common nodes ====----
        //    bool isOP = nodeContainer.GetAttributeValue("class", "!") == "postContainer opContainer";

        //    HtmlNode nodeInnerContainer;
        //    if (isOP)
        //        nodeInnerContainer = nodeContainer.SelectSingleNode("div[@class='post op']");
        //    else
        //        nodeInnerContainer = nodeContainer.SelectSingleNode("div[@class='post reply']");

        //    HtmlNode nodePostInfo = nodeInnerContainer.SelectSingleNode("div[@class='postInfo desktop']");
        //    HtmlNode nodePostInfoNameBlock;
        //    if (isOP)
        //        nodePostInfoNameBlock = nodePostInfo.SelectSingleNode("span[@class='nameBlock']");
        //    else
        //        nodePostInfoNameBlock = nodePostInfo.SelectSingleNode("span[@class='nameBlock']"); //SelectSingleNode("span[@class='userInfo']").SelectSingleNode("span[@class='nameBlock']");

        //    HtmlNode nodePostInfoPostNum;
        //    if (isOP)
        //        nodePostInfoPostNum = nodePostInfo.SelectSingleNode("span[@class='postNum']");
        //    else
        //        nodePostInfoPostNum = nodePostInfo.SelectSingleNode("span[@class='postNum desktop']");

        //    HtmlNode nodeEmail = nodePostInfoNameBlock.SelectSingleNode("a[@href]");

        //    //----==== ID ====----
        //    string ID = nodeContainer.GetAttributeValue("id", "!").Substring(2);

        //    //----==== ThreadID ====----
        //    HtmlNode nodePostInfoPostNumA = nodePostInfoPostNum.SelectSingleNode("a[1]");
        //    string threadIDAttribute = nodePostInfoPostNumA.GetAttributeValue("href", "!");
        //    int threadIDLength = threadIDAttribute.IndexOf("#");
        //    string ThreadID = threadIDAttribute.Substring(0, threadIDLength);

        //    //----==== Time ====----
        //    HtmlNode nodeTime = nodePostInfo.SelectSingleNode("span[@class='dateTime']");
        //    string utc = nodeTime.GetAttributeValue("data-utc", "!");
        //    DateTime Time = Thread.UnixTimeStampToDateTime(double.Parse(utc));

        //    //----==== Comment ====----
        //    HtmlNode nodeBlockQuote = nodeInnerContainer.SelectSingleNode("blockquote");
        //    //nodeBlockQuote.InnerHtml = nodeBlockQuote.InnerHtml.Replace("<br>", "\n");
        //    string Comment = System.Web.HttpUtility.HtmlDecode(nodeBlockQuote.InnerText);
        //    //Comment = Comment.Replace("&gt;", ">");
        //    //Comment = Comment.Replace("&#44;", ",");

        //    //----==== Email ====----
        //    string Email;
        //    if (nodeEmail == null)
        //        Email = string.Empty;
        //    else
        //        Email = nodeEmail.GetAttributeValue("href", "!");

        //    //----==== Name ====----
        //    string Name;
        //    if (Email == string.Empty)
        //        Name = nodePostInfoNameBlock.SelectSingleNode("span[@class='name']").InnerText;
        //    else
        //        Name = nodePostInfoNameBlock.SelectSingleNode("a[@href]").SelectSingleNode("span[@class='name']").InnerText;

        //    //----==== Subject ====----
        //    string Subject;
        //    if (isOP)
        //        Subject = nodePostInfo.SelectSingleNode("span[@class='subject']").InnerText;
        //    else
        //        Subject = nodePostInfo.SelectSingleNode("span[@class='subject']").InnerText; //SelectSingleNode("span[@class='userInfo']").SelectSingleNode("span[@class='subject']").InnerText;

        //    //----==== Tripcode ====----
        //    string Tripcode;
        //    HtmlNode nodeTripcode;

        //    if (Email == string.Empty)
        //    {
        //        nodeTripcode = nodePostInfoNameBlock.SelectSingleNode("span[@class='postertrip']");
        //    }
        //    else
        //    {
        //        nodeTripcode = nodeEmail.SelectSingleNode("span[@class='postertrip']");
        //    }
        //    if (nodeTripcode == null) //no tripcode
        //        Tripcode = "";
        //    else
        //        Tripcode = nodeTripcode.InnerText;

        //    //----==== File ====----
        //    AttachedFile File = null;
        //    HtmlNode nodeFile = nodeInnerContainer.SelectSingleNode("div[@class='file']");

        //    bool DeletedFile = false;

        //    if (nodeFile != null)
        //    {
        //        HtmlNode nodeFileInfo = nodeFile.SelectSingleNode("div[@class='fileInfo']");
        //        if (nodeFileInfo == null)
        //            DeletedFile = true;
        //        else
        //        {
        //            HtmlNode nodeFileText = nodeFileInfo.SelectSingleNode("span[@class='fileText']");
        //            bool IsSpoilered = nodeFile.SelectSingleNode("a").GetAttributeValue("class", "!") == "fileThumb imgspoiler";

        //            HtmlNode nodeFileThumb;
        //            if (IsSpoilered)
        //            {
        //                nodeFileThumb = nodeFile.SelectSingleNode("a[@class='fileThumb imgspoiler']");
        //            }
        //            else
        //            {
        //                nodeFileThumb = nodeFile.SelectSingleNode("a[@class='fileThumb']");
        //            }

        //            string fileURL = "http:" + nodeFileText.SelectSingleNode("a[@href]").GetAttributeValue("href", "!");
        //            string fileChanFilename = nodeFileText.SelectSingleNode("a[@href]").InnerText;
        //            string fileOriginalFilename;

        //            if (IsSpoilered)
        //                fileOriginalFilename = nodeFileText.GetAttributeValue("title", "!");
        //            else
        //                fileOriginalFilename = nodeFileText.SelectSingleNode("span[@title]").GetAttributeValue("title", "!");
        //            string fileMd5 = nodeFileThumb.SelectSingleNode("img[@src]").GetAttributeValue("data-md5", "!");
        //            string fileFilesize = nodeFileThumb.SelectSingleNode("img[@src]").GetAttributeValue("alt", "!");

        //            File = new AttachedFile(fileURL, fileChanFilename, fileOriginalFilename, fileMd5, fileFilesize, IsSpoilered);
        //        }
        //    }

        //    Post returning = new Post(ID, ThreadID, Time, Comment, Name, Email, Subject, Tripcode, File, DeletedFile);
        //    return returning;
        //}
        */
    }
}